<!DOCTYPE html>
<html>
<head>
<title>第八章 部署k8s-v1.7.6 node 节点</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */

/* RESET
=============================================================================*/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
}

/* BODY
=============================================================================*/

body {
  font-family: Helvetica, arial, freesans, clean, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 28px;
  color: #000;
}

h2 {
  font-size: 24px;
  border-bottom: 1px solid #ccc;
  color: #000;
}

h3 {
  font-size: 18px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777;
  font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
  margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
  color: #4183C4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
  padding-left: 30px;
}

ul li > :first-child, 
ol li > :first-child, 
ul li ul:first-of-type, 
ol li ol:first-of-type, 
ul li ol:first-of-type, 
ol li ul:first-of-type {
  margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt>:first-child {
  margin-top: 0px;
}

dl dt>:last-child {
  margin-bottom: 0px;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd>:first-child {
  margin-top: 0px;
}

dl dd>:last-child {
  margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
  font-size: 12px;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
  margin: 0 0px;
  padding: 0px 0px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre>code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #ccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}

kbd {
    -moz-border-bottom-colors: none;
    -moz-border-left-colors: none;
    -moz-border-right-colors: none;
    -moz-border-top-colors: none;
    background-color: #DDDDDD;
    background-image: linear-gradient(#F1F1F1, #DDDDDD);
    background-repeat: repeat-x;
    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
    border-image: none;
    border-radius: 2px 2px 2px 2px;
    border-style: solid;
    border-width: 1px;
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    line-height: 10px;
    padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
  border-left: 4px solid #DDD;
  padding: 0 15px;
  color: #777;
}

blockquote>:first-child {
  margin-top: 0px;
}

blockquote>:last-child {
  margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
  clear: both;
  margin: 15px 0;
  height: 0px;
  overflow: hidden;
  border: none;
  background: transparent;
  border-bottom: 4px solid #ddd;
  padding: 0;
}

/* TABLES
=============================================================================*/

table th {
  font-weight: bold;
}

table th, table td {
  border: 1px solid #ccc;
  padding: 6px 13px;
}

table tr {
  border-top: 1px solid #ccc;
  background-color: #fff;
}

table tr:nth-child(2n) {
  background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
  max-width: 100%
}
</style>
</head>
<body>
<h3>部署node节点</h3>
<p>kubernetes node 节点包含如下组件：</p>
<ul>
<li>Flanneld：使用flanneld-0.8 支持阿里云 host-gw模式，以获取最佳性能。</li>
<li>Docker17.07.0-ce：docker的安装很简单，这里也不说了。</li>
<li>kubelet</li>
<li>kube-proxy</li>
<li>下面着重讲kubelet和kube-proxy的安装，同时还要将之前安装的flannel集成TLS验证。</li>
</ul>
<p>注意：每台 node 上都需要安装 flannel，master 节点上可以不必安装。</p>
<h3>检查目录和文件</h3>
<p>我们再检查一下三个节点上，经过前几步操作生成的配置文件。</p>
<pre><code># ls /etc/kubernetes/ssl/
admin-key.pem  admin.pem  ca-key.pem  ca.pem  kube-proxy-key.pem  kube-proxy.pem  kubernetes-key.pem  kubernetes.pem
# ls /etc/kubernetes/
apiserver  bootstrap.kubeconfig  config  controller-manager  kube-proxy.kubeconfig  scheduler  ssl  token.csv
</code></pre>

<h3>flannel 网络架构图</h3>
<p><img src="https://i.imgur.com/aegDizo.jpg" alt="image" />
<img src="https://i.imgur.com/zWmGltI.jpg" alt="image" />
<img src="http://img.blog.csdn.net/20170424152830472?/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDI3ODkyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="images" /></p>
<h3>配置安装Flanneld，默认使用yum 安装。需要替换二进制 flanneld</h3>
<p>下载flanned-0.8 binary.</p>
<pre><code>yum install flanneld -y
wget https://github.com/coreos/flannel/releases/download/v0.8.0/flanneld-amd64 
chmod +x flanneld-amd64
cp flanneld-amd64 /usr/bin/flanneld




service配置文件/usr/lib/systemd/system/flanneld.service

cat  /usr/lib/systemd/system/flanneld.service 
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/flanneld
EnvironmentFile=-/etc/sysconfig/docker-network
ExecStart=/usr/bin/flanneld-start $FLANNEL_OPTIONS
ExecStartPost=/usr/libexec/flannel/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
</code></pre>

<p>/etc/sysconfig/flanneld配置文件    
</p>
<pre><code> cat  /etc/sysconfig/flanneld 
# Flanneld configuration options

# etcd url location.  Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS=&quot;http://172.16.200.100:2379,http://172.16.200.101:2379,http://172.16.200.102:2379&quot;

# etcd config key.  This is the configuration key that flannel queries
# For address range assignment
ETCD_PREFIX=&quot;/kube-centos/network&quot;
FLANNEL_ETCD_KEY=&quot;/kube-centos/network&quot;
ACCESS_KEY_ID=XXXXXXX
ACCESS_KEY_SECRET=XXXXXXX
# Any additional options that you want to pass
#FLANNEL_OPTIONS=&quot; -iface=eth0  -log_dir=/data/logs/kubernetes --logtostderr=false --v=2&quot;
#FLANNEL_OPTIONS=&quot;-etcd-cafile=/etc/kubernetes/ssl/ca.pem -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem&quot;
</code></pre>

<ul>
<li>设置etcd网络，主要是flannel用于分别docker的网络，‘/coreos.com/network/config’ 这个字段必须与flannel中的&quot;FLANNEL<em>ETCD</em>KEY=&quot;/coreos.com/network&quot; 保持一致    
</li>
<li>阿里云VPN 网络模式详细配置可参考 <a href="https://github.com/coreos/flannel/blob/master/Documentation/alicloud-vpc-backend.md">AliCloud VPC Backend for Flannel</a></li>
</ul>
<p>在etcd中创建网络配置
执行下面的命令为docker分配IP地址段。</p>
<pre><code>etcdctl mkdir /kube-centos/network
etcdctl  mk /kube-centos/network/config '{&quot;Network&quot;:&quot;10.24.0.0/16&quot;,&quot;Backend&quot;:{&quot;Type&quot;:&quot;ali-vpc&quot;}}'
etcdctl  mk /kube-centos/network/config '{&quot;Network&quot;:&quot;10.24.0.0/16&quot;,&quot;Backend&quot;:{&quot;Type&quot;:&quot;host-gw&quot;}}'
</code></pre>

<h3>安装kubernetes-cni 依赖kublete 强制安装</h3>
<pre><code>rpm -ivh --force kubernetes-cni-0.5.1-0.x86_64.rpm --nodeps   
</code></pre>

<p>配置cni 插件</p>
<pre><code>mkdir -p  /etc/cni/net.d
cat &gt; /etc/cni/net.d/10-flannel.conf &lt;&lt; EOF
 {
  &quot;name&quot;: &quot;cbr0&quot;,
  &quot;type&quot;: &quot;flannel&quot;,
  &quot;delegate&quot;: {
    &quot;isDefaultGateway&quot;: true,
    &quot;forceAddress&quot;: true,
    &quot;bridge&quot;: &quot;cni0&quot;,
    &quot;mtu&quot;: 1500
  }
}    
EOF
</code></pre>

<p><strong>cat /etc/sysctl.d/k8s.conf  内核参数修改</strong></p>
<pre><code>net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1    
sysctl -p  /etc/sysctl.d/k8s.conf    
</code></pre>

<p><strong>查看获取的地址段</strong></p>
<pre><code>#etcdctl ls /kube-centos/network/subnets
/kube-centos/network/subnets/10.24.15.0-24
/kube-centos/network/subnets/10.24.38.0-24    
</code></pre>

<h3>安装和配置 kubelet</h3>
<p>kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求，需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper cluster 角色(role)， 然后 kubelet 才能有权限创建认证请求(certificate signing requests)：</p>
<pre><code>cd /etc/kubernetes
kubectl create clusterrolebinding kubelet-bootstrap \
  --clusterrole=system:node-bootstrapper \
  --user=kubelet-bootstrap
</code></pre>

<p><strong>创建 kubelet 的service配置文件</strong></p>
<pre><code># cat  /usr/lib/systemd/system/kubelet.service 
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/local/kubernetes/server/bin/kubelet \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBELET_API_SERVER \
            $KUBELET_ADDRESS \
            $KUBELET_PORT \
            $KUBELET_HOSTNAME \
            $KUBE_ALLOW_PRIV \
            $KUBELET_POD_INFRA_CONTAINER \
            $KUBELET_ARGS
Restart=on-failure

[Install]
WantedBy=multi-user.target
</code></pre>

<blockquote>
<p>kubelet的配置文件/etc/kubernetes/kubelet。其中的IP地址更改为你的每台node节点的IP地址。
注意：/var/lib/kubelet需要手动创建。    
</p>
</blockquote>
<p><strong>kubelet 配置文件</strong></p>
<pre><code>cat  &gt; /etc/kubernetes/kubelet &lt;&lt; EOF
###
### kubernetes kubelet (minion) config
##
### The address for the info server to serve on (set to 0.0.0.0 or &quot;&quot; for all interfaces)
KUBELET_ADDRESS=&quot;--address=172.16.200.100&quot;
##
### The port for the info server to serve on
##KUBELET_PORT=&quot;--port=10250&quot;
##
### You may leave this blank to use the actual hostname
KUBELET_HOSTNAME=&quot;--hostname-override=172.16.200.100&quot;
##
### location of the api-server
#KUBELET_API_SERVER=&quot;--api-servers=http://172.16.200.100:8080&quot;
##
### pod infrastructure container
KUBELET_POD_INFRA_CONTAINER=&quot;--pod-infra-container-image=gcr.io/google_containers/pause:latest&quot;
##
### Add your own!
KUBELET_ARGS=&quot;--cgroup-driver=systemd --cluster-dns=10.254.0.2 --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig kubeconfig=/etc/kubernetes/kubelet.kubeconfig --require-kubeconfig --cert-dir=/etc/kubernetes/ssl --cluster-domain=cluster.local --hairpin-mode promiscuous-bridge --serialize-image-pulls=false --network-plugin=cni --cni-conf-dir=/etc/cni/net.d/ --cni-bin-dir=/opt/cni/bin/ --network-plugin-mtu=1500 --log-dir=/data/logs/kubernetes/ --v=2 --logtostderr=false&quot;
EOF
</code></pre>

<ul>
<li>--address 不能设置为 127.0.0.1，否则后续 Pods 访问 kubelet 的 API 接口时会失败，因为 Pods 访问的 127.0.0.1 指向自己而不是 kubelet；</li>
<li>如果设置了 --hostname-override 选项，则 kube-proxy 也需要设置该选项，否则会出现找不到 Node 的情况；</li>
<li>--cgroup-driver 配置成 systemd，不要使用cgroup，否则在 CentOS 系统中 kubelet 讲启动失败。docker修改cgroup启动参数 --exec-opt native.cgroupdriver=systemd</li>
<li>--experimental-bootstrap-kubeconfig 指向 bootstrap kubeconfig 文件，kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求；</li>
<li>管理员通过了 CSR 请求后，kubelet 自动在 --cert-dir 目录创建证书和私钥文件(kubelet-client.crt 和 kubelet-client.key)，然后写入 --kubeconfig 文件；</li>
<li>建议在 --kubeconfig 配置文件中指定 kube-apiserver 地址，如果未指定 --api-servers 选项，则必须指定 --require-kubeconfig 选项后才从配置文件中读取 kube-apiserver 的地址，否则 kubelet 启动后将找不到 kube-apiserver (日志中提示未找到 API Server），kubectl get nodes 不会返回对应的 Node 信息;</li>
<li>--cluster-dns 指定 kubedns 的 Service IP(可以先分配，后续创建 kubedns 服务时指定该 IP)，--cluster-domain 指定域名后缀，这两个参数同时指定后才会生效；</li>
<li>--cluster-domain 指定 pod 启动时 /etc/resolve.conf 文件中的 search domain ，起初我们将其配置成了 cluster.local.，这样在解析 service 的 DNS 名称时是正常的，可是在解析 headless service 中的 FQDN pod name 的时候却错误，因此我们将其修改为 cluster.local，去掉嘴后面的 ”点号“ 就可以解决该问题，关于 kubernetes 中的域名/服务名称解析请参见我的另一篇文章。</li>
<li>--kubeconfig=/etc/kubernetes/kubelet.kubeconfig中指定的kubelet.kubeconfig文件在第一次启动kubelet之前并不存在，请看下文，当通过CSR请求后会自动生成kubelet.kubeconfig文件，如果你的节点上已经生成了~/.kube/config文件，你可以将该文件拷贝到该路径下，并重命名为kubelet.kubeconfig，所有node节点可以共用同一个kubelet.kubeconfig文件，这样新添加的节点就不需要再创建CSR请求就能自动添加到kubernetes集群中。同样，在任意能够访问到kubernetes集群的主机上使用kubectl --kubeconfig命令操作集群时，只要使用~/.kube/config文件就可以通过权限认证，因为这里面已经有认证信息并认为你是admin用户，对集群拥有所有权限。</li>
<li>KUBELET<em>POD</em>INFRA_CONTAINER 是基础镜像容器，需要翻墙下载。</li>
<li>--network-plugin=cni 启用cni 管理docker 网络</li>
<li>-cni-conf-dir=/etc/cni/net.d/ CNI 配置路径</li>
<li>注意 需要修改docker cgroup 驱动方式： --exec-opt native.cgroupdriver=systemd</li>
</ul>
<blockquote>
<p>kubelet 依赖启动配置文件 bootstrap.kubeconfig</p>
</blockquote>
<pre><code>systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
systemctl status kubelet
</code></pre>

<p>通过 kublet 的 TLS 证书请求</p>
<p>kubelet 首次启动时向 kube-apiserver 发送证书签名请求，必须通过后 kubernetes 系统才会将该 Node 加入到集群。</p>
<p>查看未授权的 CSR 请求</p>
<pre><code># kubectl get csr
NAME                                                   AGE       REQUESTOR           CONDITION
node-csr-8I8soRqLhxiH2nThkgUsL2oIaKyh15AuNOVgJddWBqA   2s        kubelet-bootstrap   Pending
node-csr-9byGSZPAX0eT60qME8_2PIZ0Q4GkDTFG-1tvPhVaH40   49d       kubelet-bootstrap   Approved,Issued
node-csr-DpvCEHT98ARavxjdLpa_yl_aNGddNTAX07MEVSAjnUM   4d        kubelet-bootstrap   Approved,Issued
node-csr-nAOtjarW3mJ3boQ3AtaeGCbQYbW_jo8AGscFnk1uxqw   8d        kubelet-bootstrap   Approved,Issued
node-csr-sgI8CYnTFQZqaZg9wdJP6OabqBiNA0DpZ5Z0wCCl4bQ   54d       kubelet-bootstrap   Approved,Issued
</code></pre>

<p>通过 CSR 请求</p>
<pre><code>kubectl certificate approve node-csr-8I8soRqLhxiH2nThkgUsL2oIaKyh15AuNOVgJddWBqA
</code></pre>

<p>查看 通过的node</p>
<pre><code>kubectl  get node
NAME             STATUS    AGE       VERSION
172.16.200.206   Ready     11m       v1.7.6
172.16.200.209   Ready     49d       v1.7.6
172.16.200.216   Ready     4d        v1.7.6
</code></pre>

<p>自动生成了 kubelet.kubeconfig 文件和公私钥</p>
<pre><code>ls -l /etc/kubernetes/kubelet.kubeconfig
</code></pre>

<blockquote>
<p>注意：假如你更新kubernetes的证书，只要没有更新token.csv，当重启kubelet后，该node就会自动加入到kuberentes集群中，而不会重新发送certificaterequest，也不需要在master节点上执行kubectl certificate approve操作。前提是不要删除node节点上的/etc/kubernetes/ssl/kubelet*和/etc/kubernetes/kubelet.kubeconfig文件。否则kubelet启动时会提示找不到证书而失败。</p>
</blockquote>
<h3>配置 kube-proxy</h3>
<p>创建 kube-proxy 的service配置文件</p>
<p>文件路径/usr/lib/systemd/system/kube-proxy.service</p>
<pre><code>cat  &gt; /usr/lib/systemd/system/kube-proxy.service &lt;&lt; EOF
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/local/kubernetes/server/bin/kube-proxy \
        $KUBE_LOGTOSTDERR \
        $KUBE_LOG_LEVEL \
        $KUBE_MASTER \
        $KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF
</code></pre>

<p>kube-proxy配置文件/etc/kubernetes/proxy</p>
<pre><code>cat  &gt; /etc/kubernetes/proxy &lt;&lt; EOF
###
# kubernetes proxy config

# default config should be adequate

# Add your own!
KUBE_PROXY_ARGS=&quot;--bind-address=172.16.200.100 --hostname-override=172.16.200.100 --kube-api-burst=50 --kube-api-qps=20  --master=http://172.16.200.100:8080 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16 --log-dir=/data/logs/kubernetes/ --v=2 --logtostderr=false&quot;
EOF
</code></pre>

<ul>
<li>--hostname-override 参数值必须与 kubelet 的值一致，否则 kube-proxy 启动后会找不到该 Node，从而不会创建任何 iptables 规则；</li>
<li>kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量，指定 --cluster-cidr 或 --masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT；</li>
<li>--kubeconfig 指定的配置文件嵌入了 kube-apiserver 的地址、用户名、证书、秘钥等请求和认证信息；</li>
</ul>
<h2>- 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定，该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限；</h2>
<h3>启动 kube-proxy</h3>
<pre><code>systemctl daemon-reload
systemctl enable kube-proxy
systemctl start kube-proxy
systemctl status kube-proxy
</code></pre>


</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
